//---------------[CSE420]----------[Project #4]----------[Part #2]---------------
#include <stdio.h>
#include <string.h>
#include <spu_mfcio.h>
#include "common.h"


//------------------------------[Global Variables]-------------------------------
int matrixA_InSPE[NUM_OF_ROWS][NUM_OF_COLS]  QWORD_ALIGNED;
int matrixB_InSPE[NUM_OF_ROWS][NUM_OF_COLS]  QWORD_ALIGNED;
int matrixC_InSPE[NUM_OF_ROWS][NUM_OF_COLS]  QWORD_ALIGNED;
const int totalwords = MATRIX_SIZE;
const int dt_unit    = 4096;
CONTROL_BLOCK cb QWORD_ALIGNED;


//----------------------------[Forward Declarations]-----------------------------
void readControlBlock(uint64_t *argp);
void readDataBlock   (int *spuptr, int *ppuptr);
void writeDataBlock  (int *spuptr, int *ppuptr);


//------------------------------------[Main]-------------------------------------
int main(uint64_t speid, uint64_t argp, uint64_t envp)
{
	int sum;
  
	/* Read input data */
	readControlBlock(&argp);
	readDataBlock   (&matrixA_InSPE[0][0], cb.dataA);
	readDataBlock   (&matrixB_InSPE[0][0], cb.dataB);

	/* Multiply matrices */
	for(int row = 0; row < NUM_OF_ROWS; row++)
	{
		for(int col = 0; col < NUM_OF_COLS; col++)
		{
			sum = 0;
			for(int i = 0; i < NUM_OF_ROWS; i++)
			{
				sum += matrixA_InSPE[row][i] * matrixB_InSPE[i][col];
			}
			matrixC_InSPE[row][col] = sum;
		}
	}

	/* Write output data */
	writeDataBlock(&matrixC_InSPE[0][0], cb.dataC);
}


//---------------------------------[Functions]-----------------------------------
/* Reads control block data from PPE */
void readControlBlock(uint64_t *argp)
{
	mfc_get(&cb, *argp, sizeof(cb), 0, 0, 0);
	mfc_write_tag_mask(1 << 0);
	mfc_read_tag_status_all();
}

/* Reads data block from PPE */
void readDataBlock(int *spuptr, int *ppuptr)
{
	int *spulast = spuptr + totalwords;

	while ( spuptr < spulast )
	{
		int nwords = ( spuptr + dt_unit > spulast ) ? (spulast - spuptr) : dt_unit;

		mfc_get(spuptr, ppuptr, 4 * nwords, 0, 0, 0);
		mfc_write_tag_mask(1 << 0);
		mfc_read_tag_status_all();
		spuptr += dt_unit;
		ppuptr += dt_unit;
	}
}

/* Writes data block to PPE */
void writeDataBlock(int *spuptr, int *ppuptr)
{
	int *spulast = spuptr + totalwords;

    while ( spuptr < spulast )
    {
    	int nwords = ( spuptr + dt_unit > spulast ) ? (spulast - spuptr) : dt_unit;

    	mfc_put(spuptr, ppuptr, 4 * nwords, 2, 0, 0);
    	mfc_write_tag_mask(1 << 2);
    	mfc_read_tag_status_all();
    	spuptr += dt_unit;
    	ppuptr += dt_unit;
    }
}
//------------------------------------[END]--------------------------------------
